<?php
	
	namespace Aspic\View;
	
	use Aspic as A;
	
	/**
	* 
	* Provide simple functionnalities to display data into a HTML table
	* 
	*/
	class HtmlTable {

		use A\Preset\OptionTrait {
			__construct as optionTraitInit;
		}
		
		protected $data;
		protected $dataHeaders;
		
		protected $_attributes;
		
		/**
		 *
		 * @var string default value when $row[index] is not set
		 */
		protected $_defaultValue;
		protected $_noDataMessage;
		protected $_noDataColumnClass;
		protected $_rowsColors;
		
		protected $_formatChar;
		protected $_rowformatStartChar;
		protected $_rowformatEndChar;
		
		/**
		* @var function A function that must return an array of attribute to set the given row, params: ($row)
		*/
		protected $_rowAttributesFunc;
		
		/**
		* @var string Function in which data are going to pass into, to be displayed in the html page (for example htmlentities)
		*/
		protected $_escapeFunction;
		
		protected $_dataModelDefaultColOptions = array(
			'ignore' => false,
			'type' => 'text', 
			'params' => array(),
		);


		/**
		 * @param type $data Array containing values to be displayed
		 * @param type $attributes Attributes of the table
		 */
		public function __construct($data = array(), $attributes = array()) {
			$this->initOptionTrait();
			
			$this->setData($data);
			$this->dataHeaders = array();
			
			$this->opt('defaultValue', '');
			$this->opt('rowsColors', array());
			$this->opt('noDataColumnClass', '');
			$this->opt('noDataMessage', 'No data');
			$this->opt('attributes', $attributes);
			$this->opt('escapeFunction', 'Aspic\Security\Security::escape');
			$this->opt('rowAttributesFunc', null);
			
			$this->opt('formatChar', '?');
			$this->opt('rowformatStartChar', '{');
			$this->opt('rowformatEndChar', '}');
		}
		
		public function initFromDataModel(A\Model\DataModel $dataModel, array $options = array()) {
			foreach ($dataModel->getCols() as $colName => $col) {
				$colOptions = $this->opt('dataModelDefaultColOptions');
				
				if (isset($options[$colName])) {
					$colOptions = array_merge($colOptions, $options[$colName]);
					
					if (!$colOptions['ignore']) {
						if (!isset($options[$colName]['type']) AND $col['specialType']) {
							$st = $col['specialType'];

							if ($st == A\Model\DataModel::ST_DATE) {
								$colOptions['type'] = 'date';
							}
						}
					
						$this->addHeader($colName, $col['label'], $colOptions['type'], $colOptions['params']);
					}
				}
				
				
			}
		}
		
		public function setData($data) {
			if(is_null($data))
				$data = array();
			
			$this->data = $data;
		}
		
		public function addRow($row) {
			$this->data[] = $row;
		}
		
		public function addHeaderFunction($label, $function, array $params = array()) {
			array_unshift($params, $function);
			
			return $this->addHeader(null, $label, 'function', $params);
		}
		
		/**
		* @param array $params Array of params (format, link)
		*/
		public function addHeader($index, $label, $type = 'text', array $params = array()) {
			$hTypeMethodName = 'headerType_'.$type;
			
			if(!is_string($index) AND !is_null($index)) {
				throw new A\Exception('index param must be a string (or null)');
			}
			elseif(!method_exists($this, $hTypeMethodName)) {
				throw new A\Exception('Header type "'.$type.'" does not exists');
			}
			
			$defaultParams = array(
				'preFormat' => null, // Same as format but execute before passing in headers types (could only be used when index is set)
				'format' => null, 
				'linkLabel' => null, 
				'default' => $this->opt('defaultValue'), // Default value when $row[index] is not set
				'attributes' => array(), 
				'filterFunc' => null
			);
			
			$header = new \stdclass;
			
			$header->index = $index;
			$header->label = $label;
			$header->type = $type;
			$header->params = array_merge($defaultParams, $params);
			
			$this->dataHeaders[] = $header;
			
			return $this;
		}
		
		/**
		* Set differents rows colors
		* 
		* @param string $colors,... Colors list
		*/
		public function setRowsColors($colors) {
			$this->opt('rowsColors', func_get_args());
			
			return $this;
		}
		
		/**
		* Define the message which is going to be showed when there is no data
		* 
		* @param string $msg The message
		* @param string $class The html class of the message
		* 
		*/
		public function setNoDataMessage($msg, $class = '') {
			$this->noDataMessage = $msg;
			$this->noDataColumnClass = $class;
			
			return $this;
		}
		
		/**
		 * Return the value of a column from its type
		 * @param type $row The current row in data source
		 * @param type $headerCol Column information (header object)
		 * @return string Column value
		 */
		protected function getCellValue($row, $headerCol) {
			$params = $headerCol->params;
			
			// Index could be null if type is a function or rowFormat
			if(!is_null($headerCol->index)) {
				if(isset($row[$headerCol->index])) {
					$item = $row[$headerCol->index];
				}
				else {
					$item = $params['default'];
				}
			}
			
			if(is_string($params['preFormat']) AND !is_null($headerCol->index)) {
				$row[$headerCol->index] = A\UString::fromArgsArray($params['preFormat'], array($item), $this->opt('formatChar'));
			}
			
			$hTypeMethodName = 'headerType_'.$headerCol->type;
			// var_dump($params);
			$hFuncParams = A\UArray::filterByKeyType($params);
//			var_dump($hFuncParams);
			
			array_unshift($hFuncParams, $row, $headerCol->index, $params['default']);
//			var_dump($hFuncParams);
			$value = call_user_func_array(array($this, $hTypeMethodName), $hFuncParams);
			
			if(is_string($params['linkLabel'])) {
				$label = $params['linkLabel'];
				$href = $item;
				
				$value = '<a href="'.$href.'">'.$label.'</a>';
			}
			
			if(is_string($params['format'])) {
				$value = A\UString::fromArgsArray($params['format'], array($value), $this->opt('formatChar'));
			}
			
			if(is_callable($params['filterFunc'])) {
				$value = call_user_func($params['filterFunc'], $value);
			}
			
			return $value;
		}
		
		/**
		* Get html output of the table
		* 
		* @return string Html output
		*/
		public function out() {
			$html = '<table '.A\UString::implodeHtmlAttributes($this->opt('attributes')).'>'."\n";
			
			$html .= '	<thead>'."\n";
			
			$html .= '		<tr>'."\n";
					
			foreach($this->dataHeaders as $headerCol) {
				$html .= '			<th>'.$headerCol->label.'</th>'."\n";
			}
					
			$html .= '		</tr>'."\n";
			
			$html .= '	</thead>'."\n";
			
			$html .= '	<tbody>'."\n";
			
			$rowIndex = 0;
			$nbRowsColors = count($this->opt('rowsColors'));
			
			if(count($this->data)) {
				foreach($this->data as $row) {
					$row = A\UArray::toArray($row);
					
					if($nbRowsColors AND isset($this->rowsColors[$rowIndex%$nbRowsColors]) AND $this->rowsColors[$rowIndex%$nbRowsColors]) {
						$style = ' style="background-color: '.$this->rowsColors[$rowIndex%count($this->rowsColors)].'"';
					}
					else {
						$style = '';
					}
					
					$rowFunc = $this->opt('rowAttributesFunc');
					
					if($rowFunc) {
						$attributesStr = A\UString::implodeHtmlAttributes($rowFunc($row));
					}
					else {
						$attributesStr = '';
					}
					
					$html .= '		<tr'.$style.' '.$attributesStr.'>'."\n";
					
					$headerIndex = 0;
					
					foreach($this->dataHeaders as $headerCol) {
						$value = $this->getCellValue($row, $headerCol);
						$colAttributesStr = A\UString::implodeHtmlAttributes($headerCol->params['attributes']);
						
						$html .= '			<td'.$colAttributesStr.'>'.$value.'</td>'."\n";
						
						$headerIndex++;
					}
						
					$html .= '	</tr>'."\n";
					
					$rowIndex++;
				}
			
			}
			else {
				$html .= '		<tr>'."\n";
				
				$html .= '			<td class="'.$this->noDataColumnClass.'" colspan="'.count($this->dataHeaders).'">'.$this->noDataMessage.'</td>'."\n";
				
				$html .= '	</tr>'."\n";
			}
			
			$html .= '	</tbody>'."\n";
			
			$html .= '</table>'."\n";
			
			return $html;
		}
		
		public function __toString() {
			return $this->out();
		}
		
		##### HEADER TYPE FUNCTIONS #####
		
		protected function headerType_text($row, $index, $default) {
			$value = A\Util::ifSet($row[$index], $default);
			$value = call_user_func($this->opt('escapeFunction'), $value);
			
			return $value;
		}
		
		protected function headerType_image($row, $index, $default, $attributes = array()) {
			$value = A\Util::ifSet($row[$index], $default);
			
			if(isset($attributes['src'])) {
				unset($attributes['src']);
			}
			
			$attributesStr = A\UString::implodeHtmlAttributes($attributes);
			
			if(strlen($attributesStr)) {
				$attributesStr = ' '.$attributesStr;
			}	
			
			$value = '<img src="'.htmlspecialchars($value).'"'.$attributesStr.' />';
			
			return $value;
		}
		
		protected function headerType_date($row, $index, $default, $toFormat, $fromFormat = 'Y-m-d') {
			$value = A\Util::ifSet($row[$index], $default);
			// var_dump($value);
			if (!$value instanceof \DateTime) {
				$date = \DateTime::createFromFormat($fromFormat, $value);
			}
			else {
				$date = $value;
			}
			
			$value = $date->format($toFormat);
			
			return $value;
		}
		
		protected function headerType_rowFormat($row, $index, $default, $rowFormat) {
			$row = array_map($this->opt('escapeFunction'), $row);
			
			$value = A\UString::fromArgsArray(
				$rowFormat, 
				$row, 
				$this->opt('formatChar'), 
				$this->opt('rowformatStartChar'), 
				$this->opt('rowformatEndChar')
			);
			
			return $value;
		}
		
		protected function headerType_function($row, $index, $default, $func, $funcParams = array()) {
			if(!is_array($funcParams)) {
				$funcParams = array();
			}
			// var_dump(func_get_args());
			array_unshift($funcParams, $row);
			$value = call_user_func_array($func, $funcParams);
			
			return $value;
		}
		
	}	
	
?>